#include <iostream>
#include <vector>
#include <cstring>
#include <cerrno>
#include <ctime>
#include <cstdlib>
#include <unordered_map>
#include <string>

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

using namespace std;

typedef pair<int, int> element;       //进程pid和管道fd的映射，可以知道要写给那个管道
unordered_map<uint32_t, string> info; //管理任务编号和任务的关系，给父进程核实子进程用的

typedef void (*Func)();
//先实现单个进程传随机任务
void func1()
{
    cout << "这是一个处理数据库的任务\n"
         << endl;
}
void func2()
{
    cout << "这是一个处理网络的任务\n"
         << endl;
}
void func3()
{
    cout << "这是一个处理本地代码的任务\n"
         << endl;
}

// void Loadfunc(vector<Func> &func_arr) //统一管理函数任务
// {
//     func_arr.push_back(func1);
//     func_arr.push_back(func2);
//     func_arr.push_back(func3);
// }

// int main()
// {
//     vector<Func> func_v;
//     Loadfunc(func_v);
//     int pipeid[2] = {0};
//     if (pipe(pipeid) == -1)
//     {
//         cout << strerror(errno) << endl;
//         return -1;
//     }
//     pid_t pid = fork();
//     if (pid == 0)
//     {
//         //子进程，读，此刻pipeid数组有两份。
//         close(pipeid[1]);
//         uint32_t operatordata = 0;
//         Func fc =nullptr;
//         while (true)
//         {
//             //按照道理：fork之后子进程的虚拟存储和父进程是一样的。但是一般不采用传指针，用就用现成的自己的函数
//             // if (read(pipeid[0], &operatordata, sizeof(operatordata)))
//             if (read(pipeid[0], &fc, sizeof(fc)))//sizeof(fc)也是一个指针的大小，当作&fc
//             {
//                 ((Func)fc)(); //获取任务码，执行指定的任务。
//                 // func_v[operatordata]();

//                 //下面测试管道大小
//             }
//             else
//             {
//                 cout << "父进程结束了，我子进程也读取完成，已经退出" << endl;
//                 break;
//             }
//         }
//         close(pipeid[0]);
//         exit(0);
//     }
//     //父进程，写
//     close(pipeid[0]);
//     srand((unsigned)time(nullptr));
//     int cnt = 0;
//     Func fc =nullptr;

//     while (cnt < 10)
//     {
//         cnt++;
//         uint32_t operatorwrite = rand() % func_v.size();
//         // write(pipeid[1], &operatorwrite, sizeof(operatorwrite));

//         // cout<<"1"<<endl;
//         fc = func_v[operatorwrite];
//         write(pipeid[1], &fc, sizeof(fc));

//         // while(true)
//         sleep(1);

//     }

//     // while (true)
//     // {
//     //     int operatorwrite = rand() % func_v.size();
//     //     write(pipeid[1], &operatorwrite, sizeof(operatorwrite));
//     //     // cnt++;
//     //     cout << cnt << endl;
//     // }
//     cout << "父进程写完了，要退出了" << endl;
//     close(pipeid[1]);

//     return 0;
// }

void Loadfunc(vector<Func> &func_arr) //统一管理函数任务
{
    info.insert({func_arr.size(), "处理数据库"});
    func_arr.push_back(func1);

    info.insert({func_arr.size(), "处理网络数据"});
    func_arr.push_back(func2);

    info.insert({func_arr.size(), "处理本地文件"});
    func_arr.push_back(func3);
}
int main()
{
    //实现多个进程的子进程
    vector<Func> func_v;
    Loadfunc(func_v);
    int pipeid[2] = {0};

    vector<element> proc_v;
    if (pipe(pipeid) != 0)
    {
        cout << strerror(errno) << endl;
        return -1;
    }
    for (int i = 0; i < 5; i++)
    {
        //创建多个管道，和不同进程相连。


        pid_t pid = fork();
        if (pid == 0)
        {
            //子进程中，读
            close(pipeid[1]);
            uint32_t operatorread = 0;
            while (true) //子进程一直读，知道另一端写完，另一端退出才可停止,而不是只在for循环里面读
            {
                if (read(pipeid[0], &operatorread, sizeof(operatorread)) != 0)
                {
                    func_v[operatorread]();
                    cout<<"进程"<<getpid()<<"获取到了任务,编号是"<<i<<endl;
                    sleep(1);//这样一直被读，写者无法写新的任务。需要进程控制
                }
                else
                {
                    close(pipeid[0]);
                    exit(0);
                    // break;
                }
            }
        }
       //父进程处理
            //更新创建的进程pid和管道fd的链接，记录到数组，方便下面插入
            close(pipeid[0]);
            proc_v.push_back(make_pair(pid, pipeid[1]));
    }
    //父进程写

    // [子进程的pid, 子进程的管道fd]

    srand((long long)time(nullptr));
    int cnt = 0;
    while (cnt < 5)
    {
        sleep(1);
        cnt++;
        // 选择一个进程, 选择进程是随机的，没有压着一个进程给任务
        // 较为均匀的将任务给所有的子进程 --- 负载均衡
        uint32_t pick = rand() % proc_v.size();

        // 选择一个任务
        uint32_t task = rand() % func_v.size();

        // 把任务给一个指定的进程
        write(proc_v[pick].second, &task, sizeof(task));

        // 打印对应的提示信息
        cout << "父进程指派任务->" << info[task] << "给进程: " << proc_v[pick].first << " 编号: " << pick << endl;
    }

    //进程等待，关闭匿名管道文件的链接
    for (int i = 0; i < proc_v.size(); i++)
    {
        close(proc_v[i].second);                            //关闭管道，让子进程可以退出
        // if (waitpid(proc_v[i].first, nullptr, 0) > 0)
        //     cout << "wait for: pid=" << proc_v[i].first << " wait success!"
        //          << "number: " << i << "\n";
    }
    for (int i = 0; i < proc_v.size(); i++)
    {
        if (waitpid(proc_v[i].first, nullptr, 0) > 0)
            cout << "wait for: pid=" << proc_v[i].first << " wait success!"
                 << "number: " << i << "\n";
    }
}